iT邦幫忙

2022 iThome 鐵人賽

DAY 22
1
Software Development

30天學會Golang系列 第 22

Day22 - Go的文檔讀寫

  • 分享至 

  • xImage
  •  

day20時,我們有用到讀取文檔的功能,當時未特別介紹,在這邊來介紹一下關於文檔的讀寫。關於文檔的讀寫,在之前有個大家很常用的函式庫 io/ioutil,該函式庫有完整的讀寫功能,不過自從 go 1.16 版之後,功能都可以透過 io 與 os 取代,功能基本都一樣,就是換了個函式庫,根據 官方 的說法如下:

Deprecated: As of Go 1.16, the same functionality is now provided by package io or package os, and those implementations should be preferred in new code. See the specific function documentation for details.

讀取文檔

讀取文檔的方式有三種方式:

  • 將文檔整份讀進記憶體
  • 逐行讀取
  • 按字元數讀取

一次性讀取文檔全部的程式碼如下:

// 讀取全部
func ReadFileAll(filePath string) []byte {
	file, err := os.Open(filePath)
	if err == nil {
		fmt.Println(err)
	}
	defer file.Close()

	data, err := io.ReadAll(file)
	if err != nil {
		fmt.Println(err)
	}

	return data
}

如果要逐行或按字元讀取,一般會額外再引用 bufio 函式庫,該函式庫實現了有緩衝的 I/O。

以下為逐行讀取的方式:

// 逐行讀取
func ReadFileLine(filePath string) []byte {
	fi, err := os.Open(filePath)
	if err != nil {
		panic(err)
	}
	defer fi.Close()

	lines := make([]byte, 0)
	r := bufio.NewReader(fi)
	for {
		line, _, c := r.ReadLine()
		if c == io.EOF {
			break
		}
		line = []byte(string(line) + "\n") // 為了與其他結果一樣而多加的一行
		lines = append(lines, line...)
	}

	return lines
}

以下為按字元讀取的方式:

// 讀取字元
func ReadFileChar(filePath string) []byte {
	fi, err := os.Open(filePath)
	if err != nil {
		panic(err)
	}
	defer fi.Close()
	r := bufio.NewReader(fi)

	chunks := make([]byte, 0)
	buf := make([]byte, 1024) //一次讀取1024個字元
	for {
		n, err := r.Read(buf)
		if err == io.EOF || err != nil {
			// fmt.Println(err)
			break
		}
		chunks = append(chunks, buf[:n]...)
	}
	return chunks
}

以效率來說,效率比較高的方式是第一種,一次性全部讀取完,但是相對來說,記憶體的佔用量也相對最高,但是當要讀取的檔案可能會很大時,不建議使用,因為可能會導致記憶體崩潰,因此讀取文件時可以自行斟酌依情況使用

寫入文檔

那寫文檔的方式就沒那麼多的區別,基本上差別主要是在於從頭開始寫,還是接續寫,相對完整的解說,可以看看 參考來源2

從頭開始寫的寫法如下:

func WriteFileCover(fileName string) {
	err := os.WriteFile(fileName, str, 0644)
	if err != nil {
		fmt.Println(err)
	}
}

接續寫的寫法如下:

func WriteFileContinue(fileName string) {
	f, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Println(err)
	}

	defer f.Close()

	_, err = f.WriteString(string(str))
	if err != nil {
		fmt.Println(err)
	}
}

那麼放到連續執行兩次的程式碼與結果分別如下:

func main() {
	// 寫入
	for i := 0; i < 2; i++ {
		WriteFileCover("day22/dat1.yaml")
		WriteFileContinue("day22/dat2.yaml")
	}
}

dat1.yaml 結果:

hello
go

dat2.yaml 結果:

hello
go
hello
go
第22天報到,讀取與寫入文檔通常會影響到整支程式的效率,所以一般來說我們會將這部分盡可能地執行越少次數越好

參考來源

  1. https://segmentfault.com/a/1190000017918542
  2. https://zhaolion.com/post/golang/package/bufio/
  3. https://pkg.go.dev/io/ioutil

代碼連結

https://github.com/luckyuho/ithome30-golang/tree/main/day22


上一篇
Day21 - Go的網路爬蟲 colly
下一篇
Day23 - Go的網頁框架 gin
系列文
30天學會Golang31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言